package com.secure.advice;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.secure.annotation.ApiSecure;
import com.secure.constant.RequestConstant;
import com.secure.constant.SeparatorConstant;
import com.secure.enums.ResponseEncryptEnum;
import com.secure.exception.ApiSecureException;
import com.secure.handler.EncryptDecryptHandler;
import com.secure.properties.SecureProperties;
import com.secure.util.PathUtil;
import com.secure.util.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Method;
import java.util.List;

/**
 * 响应加密增强
 * <p>
 * {@link ControllerAdvice} + RequestBodyAdvice/ResponseBodyAdvice
 * 结合{@link com.fasterxml.jackson.annotation.JsonView}可以控制响应体哪些字段接收和返回。
 * {@link com.fasterxml.jackson.annotation.JsonView}注解由内置的JsonViewRequestBodyAdvice
 * /JsonViewResponseBodyAdvice处理。
 *
 * @author duchao
 */
@Slf4j
@ControllerAdvice
@RequiredArgsConstructor
public class ResponseSecureAdvice implements ResponseBodyAdvice<Object> {

    private final SecureProperties secureProperties;
    private final EncryptDecryptHandler encryptDecryptHandler;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        Method method = returnType.getMethod();
        if (ObjectUtil.isNotNull(method) && method.isAnnotationPresent(ApiSecure.class)) {
            ApiSecure annotation = method.getAnnotation(ApiSecure.class);
            //注解@ApiSecure(encryptResponse=ResponseEncryptEnum.ENCRYPT)会覆盖配置文件配置
            return ResponseEncryptEnum.UNSPECIFY == annotation.encryptResponse() ? isNeedEncryptResponseFromConfig() : ResponseEncryptEnum.ENCRYPT == annotation.encryptResponse();
        }
        return false;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body == null) {
            return null;
        }

        try {
            //response.getHeaders().add(RequestConstant.ENCRYPT_RESPONSE,null);
            String bodyString = new ObjectMapper().writeValueAsString(body);
            return encryptDecryptHandler.encryptByPrivateKey(bodyString, secureProperties.getAppKey().getEncryptDecrypt().getPrivateKey());
        } catch (Exception e) {
            throw new ApiSecureException(e);
        }
    }

    /**
     * 配置中配置是否需要加密响应
     *
     * @return
     */
    public boolean isNeedEncryptResponseFromConfig() {
        List<String> patternPathList = secureProperties.getHandlerMappingPathList();
        String servletPath = RequestUtil.getCurrentRequestServletPath();
        if (CollectionUtil.isEmpty(patternPathList) || StrUtil.isBlank(servletPath)) {
            return false;
        }

        boolean isNeedEncryptResponse = false;
        for (String patternPath : patternPathList) {
            isNeedEncryptResponse = patternPath.endsWith(SeparatorConstant.COMMA.concat(RequestConstant.ENCRYPT_RESPONSE));
            if (isNeedEncryptResponse) {
                patternPath = patternPath.substring(0, patternPath.indexOf(SeparatorConstant.COMMA));
            }
            if (PathUtil.isMatch(patternPath, servletPath)) {
                break;
            }
        }
        return isNeedEncryptResponse;
    }
}
